import java.io.*;
import java.util.*;

/** 
 * Rooks - ROom boOKing System. Rooks is used to run the system as a whole and
 * contains the rooms of the system.
 */
public class Rooks
{
    // List and Map used to keep ordering on rooms
    private List<Room> roomList;
    private Map<String, Room> roomMap;

    /** 
     * Constructor calls initialise()
     */
    public Rooks()
    {
        initialise();
    }

    /** 
     * Creates space for the rooms of the system
     */
    public void initialise()
    {
        roomList = new ArrayList<Room>();
        roomMap = new HashMap<String, Room>();
    }

    /*
    // Strictly used for testing - remove in final release
    public Map<String, Room> getRoomMap()
    {
        return roomMap;
    }
    */

    // Functions

    /** 
     * Check whether persistent storage files exist and read from them if so,
     * otherwise create a new database of rooms
     */
    public void readFiles() throws Exception
    {
        File file1 = new File("rooms.txt");
        File file2 = new File("bookings.txt");

        if (file1.exists() && file2.exists())
        {
            readRoomFile(file1);
            readBookingFile(file2);
        }
        else
        {
            createRooms();
        }
    }

    /** 
     * Read room file and input into java application. Formatting of files
     * will have been created by the system and therefore no errors should
     * be emitted
     */
    public void readRoomFile(File f)
    {
        String line;
        String[] temp;

        try
        {
            BufferedReader br = new BufferedReader(new FileReader(f));

            while ((line = br.readLine()) != null)
            {
                Room room = new Room();
                temp = line.split(" ");

                room.setCapacity(Integer.parseInt(temp[1]));
                room.setOpenTime(Read.time(temp[2]));
                room.setCloseTime(Read.time(temp[3]),true);

                if (temp.length == 5)
                {
                    room.addItems(temp[4].split(","));
                }

                addRoom(room, temp[0]);
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
            System.out.println(e.getMessage());
            System.exit(1);
        }
    }

    /** 
     * Read booking file and input into java application. Formatting of files
     * will have been created by the system and therefore no errors should
     * be emitted
     */
    public void readBookingFile(File f)
    {
        String line;
        String[] temp;

        try
        {
            BufferedReader br = new BufferedReader(new FileReader(f));

            while ((line = br.readLine()) != null)
            {
                Booking booking;

                temp = line.split(" ");

                if(temp.length == 5)
                {
                    booking = new RecurringBooking();
                }
                else
                {
                    booking = new Booking();
                }

                Room room = roomMap.get(temp[0]);
                booking.setStartTime(Read.date(temp[2]));
                booking.setEndTime(Read.date(temp[3]));
                room.addBooking(booking, temp[1]);
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
            System.out.println(e.getMessage());
            System.exit(1);
        }
    }

    /** 
     * Function that runs the menu system.
     */
    public void run() throws Exception
    {
        readFiles();

        // Loops till user hits e for exit at menu system
        while (true)
        {
            System.out.println("What would you like to do?");

            // Menu system

            // If there are no rooms, then you can't query and add bookings
            if (roomMap.isEmpty() == false)
            {
                System.out.println("q (query and add booking),");

            }
            System.out.println("c (create new database - overwrites old one),");
            System.out.println("a (add new rooms),");
            System.out.println("e (exit) :-");

            String input = Read.systemIn();

            if (roomMap.isEmpty() == false)
            {
                // Query to add a booking
                if (input.equals("q"))
                {
                    queries();
                    continue;
                }
            }

            // Create new database
            if (input.equals("c"))
            {
                File roomFile = new File("rooms.txt");
                File bookingFile = new File("bookings.text");

                initialise();
                roomFile.delete();
                bookingFile.delete();
                createRooms();
                continue;
            }

            // Add new rooms
            if (input.equals("a"))
            {
                createRooms();
                continue;
            }

            // Exit
            if (input.equals("e"))
            {
                createFiles();

                File roomFile = new File("rooms.txt");
                File bookingFile = new File("bookings.txt");

                BufferedReader br = new BufferedReader(new FileReader(roomFile));

                if (br.readLine() == null)
                {
                    roomFile.delete();
                    bookingFile.delete();
                }

                break;
            }
        }
    }

    /** 
     * Add room in natural ordering as defined in compareTo() in the Room class
     */
    public void addRoom(Room room, String roomName)
    {
        int i = 0;

        room.setName(roomName);

        for (i = 0; i < roomList.size(); i++)
        {
            roomMap.put(roomName, room);
            if (roomList.get(i).compareTo(room) == 1)
            {
                roomList.add(i, room);
                break;
            }
        }

        if (i == roomList.size())
        {
            roomList.add(room);
            roomMap.put(roomName, room);
        }
    }

    /** 
     * Create persistent storage files
     */
    public void createFiles()
    {
        try
        {
            BufferedWriter roomData = new BufferedWriter(new FileWriter(
                    "rooms.txt"));

            BufferedWriter bookingData = new BufferedWriter(new FileWriter(
                    "bookings.txt"));

            for (Room room : roomList)
            {
                roomData.write(room.getName() + " " + room.getCapacity() + " "
                        + room.getOpenTime() + " " + room.getCloseTime() + " ");

                for (String item : room.getItems().keySet())
                {
                    roomData.write(item + ",");
                }
                roomData.write("\n");

                for (Booking booking : room.getOrderedBookings())
                {
                    bookingData.write(room.getName() + " "
                            + booking.getBookingName() + " "
                            + booking.printStartTime() + " "
                            + booking.printEndTime());

                    if(booking instanceof RecurringBooking)
                    {
                        bookingData.write(" recurring");
                    }

                    bookingData.write("\n");
                }
            }
            roomData.close();
            bookingData.close();
        }
        catch (Exception e)
        {
            e.printStackTrace();
            System.err.println(e.getMessage());
            e.printStackTrace();
        }
    }

    /** 
     * Create room if the user answers "y" to "Create room?"
     */
    public void createRoom() throws IOException
    {
        Room room = new Room();
        String tempInput;

        System.out.print("Room Name (no spaces) :- ");
        System.out.flush();
        room.setName(Read.systemIn());

        System.out.print("Capacity :- ");
        room.setCapacity(Read.integer());

        System.out.print("Available from (hh:mm) :- ");
        room.setOpenTime(Read.time(""));

        System.out.print("Available until (hh:mm) :- ");
        room.setCloseTime(Read.time(""),false);

        System.out.print("Features available (csv, no spaces) :- ");

        String items = Read.systemIn();

        if(items.equals("") == false)
        {
            room.addItems(items.split(","));
        }

        System.out.print("Create room? (y/n) :- ");
        tempInput = Read.yesNo();

        if (tempInput.equals("y"))
        {
            addRoom(room, room.getName());
        }

    }

    /** 
     * Calls createRoom in a loop until user doesn't want to add any more rooms
     */
    public void createRooms() throws IOException
    {
        String input;

        while (true)
        {
            System.out.print("Create new room? (y/n) :- ");

            input = Read.yesNo();

            if (input.equals("y"))
            {
                createRoom();
            }
            else
            {
                break;
            }
        }
    }

    /** 
     * Calls query() until user doesn't want to add any more bookings
     */
    public void queries() throws IOException
    {
        String input;

        while (true)
        {
            System.out.print("Create new query? (y/n) :- ");

            input = Read.yesNo();

            if (input.equals("y"))
            {
                query();
            }
            else
            {
                break;
            }
        }
    }

    /** 
     * Add booking if the user picks a room from the list of appropriate rooms
     */
    public void query() throws IOException
    {
        String tempInput;
        String features;

        int capacity;
        Booking booking = new Booking();
        Map<String, Integer> items = new HashMap<String, Integer>();

        System.out.print("Required capacity :- ");
        System.out.flush();
        capacity = Read.integer();

        System.out.print("Start date/time (DD/MM/YYYY,hh:mm) :- ");
        booking.setStartTime(Read.date(""));

        System.out.print("End date/time (DD/MM/YYYY,hh:mm) :- ");
        booking.setEndTime(Read.date(""));

        System.out.print("Do you wish to make this a recurring weekly booking? (y/n) :- ");

        if(Read.yesNo().equals("y"))
        {
            Date date1 = booking.getStartTime();
            Date date2 = booking.getEndTime();
            booking = new RecurringBooking();
            booking.setStartTime(date1);
            booking.setEndTime(date2);
            ((RecurringBooking)booking).setType("week");
        }

        System.out
                .print("Features required (csv, no spaces, enter for nothing) :- ");
        features = Read.systemIn();

        if(features.equals("") == false)
        {
            for (String item : features.split(","))
            {
                items.put(item, 1);
            }
        }

        System.out.print("Submit query? (y/n) :- ");
        tempInput = Read.yesNo();

        if (tempInput.equals("y"))
        {
            System.out.println("Relevant rooms available:-");

            for (String r : findRooms(capacity, booking.getStartTime(),
                    booking.getEndTime(), items))
            {
                System.out.print(r + " ");
            }

            System.out.println("\nEnter room wanted (enter for none) :- ");
            String tempRoomName = Read.systemIn();

            if (tempRoomName.equals("") == false)
            {
                System.out.print("Enter booking name :- ");
                String tempBookingName = Read.systemIn();
                roomMap.get(tempRoomName).addBooking(booking, tempBookingName);
            }
        }
    }

    /*
    // For testing only
    public List<Room> getOrderedRoomList()
    {
        return roomList;
    }
    */

    /**
     * Finds suitable rooms for the query entered. If there are enough suitable
     * rooms it will return a maximum of 3
     */
    public String[] findRooms(int capacity, Date startTime, Date endTime,
            Map<String, Integer> itemsWanted)
    {
        List<String> appropriateRooms = new ArrayList<String>();
        int counter = 0;

        for (Room room : roomList)
        {
            if(room.getCapacity() < capacity
                || room.containsItems(itemsWanted) == false
                || room.isClash(startTime, endTime)
                || room.isOpen(startTime, endTime) == false)
            {
                continue;
            }

            appropriateRooms.add(room.getName());

            if (++counter >= 3)
            {
                break;
            }
        }

        String[] roomsReturn = new String[appropriateRooms.size()];
        appropriateRooms.toArray(roomsReturn);

        return roomsReturn;
    }

    /**
     * Used to run the system overall
     */
    public static void main(String[] args) throws Exception
    {
        Rooks system = new Rooks();
        system.run();
    }
}
